#include <stdio.h>
#include "gc.h"
#include "gc_macros.h"

void test_forwarding()
{
	cell arena[256];
	cell ref1;

	printf("Null forwarding...\n");
	printf("  plain pointer (%p)... ", arena);
	fflush(stdout);
	arena[0] = (cell)CDR_NOT_CONS;
	ref1 = (cell)arena;
	forward(&ref1);
	printf((ref1 == (cell)arena) ? "pass (%p)\n" : "FAIL (%p)\n", ref1);
	
	printf("  tagged pointer (%p)... ", tag(arena, CDR_NEXT));
	fflush(stdout);
	arena[0] = (cell)CDR_NOT_CONS;
	arena[1] = tag(arena, CDR_NEXT);
	ref1 = (cell) (arena + 1);
	forward((cell*)ref1);
	printf((arena[1] == tag(arena, CDR_NEXT)) ? "pass (%p)\n": "fail (%p)\n", arena[1]);

	
	printf("Single forwarding...\n");
	printf("  plain pointer (%p)... ", arena);
	fflush(stdout);
	arena[0] = (cell)CDR_NOT_CONS;
	arena[2] = tag(arena, CDR_FORW);
	ref1 = (cell)(arena + 2);
	forward(&ref1);
	printf((ref1 == (cell)arena) ? "pass (%p)\n" : "FAIL (%p)\n", ref1);
	
	printf("  tagged pointer (%p)... ", tag(arena, CDR_NEXT));
	fflush(stdout);
	arena[0] = (cell)CDR_NOT_CONS;
	arena[1] = tag(arena + 2, CDR_NEXT);
	arena[2] = tag(arena, CDR_FORW);
	ref1 = (cell)(arena + 1);
	forward((cell*)ref1);
	printf((arena[1] == tag(arena, CDR_NEXT)) ? "pass (%p)\n": "fail (%p)\n", arena[1]);

	
	printf("Multiple forwarding...\n");
	printf("  plain pointer (%p)... ", arena);
	fflush(stdout);
	arena[0] = (cell)CDR_NOT_CONS;
	arena[2] = tag(arena, CDR_FORW);
	arena[3] = tag(arena + 2, CDR_FORW);
	ref1 = (cell)(arena + 3);
	forward(&ref1);
	printf((ref1 == (cell)arena) ? "pass (%p)\n" : "FAIL (%p)\n", ref1);
	
	printf("  tagged pointer (%p)... ", tag(arena, CDR_NEXT));
	fflush(stdout);
	arena[0] = (cell)CDR_NOT_CONS;
	arena[1] = tag(arena + 3, CDR_NEXT);
	arena[2] = tag(arena, CDR_FORW);
	arena[3] = tag(arena + 2, CDR_FORW);
	ref1 = (cell)(arena + 1);
	forward((cell*)ref1);
	printf((arena[1] == tag(arena, CDR_NEXT)) ? "pass (%p)\n": "fail (%p)\n", arena[1]);
	
	
	printf("\n");
}
void test_cons_access()
{
	cell arena[256];
	cell ref1, rtn;
	printf("Normal cons cell...\n");
	arena[0] = (cell)(arena + 2);
	arena[1] = (cell)(arena + 3);
	
	ref1 = (cell)arena;
	printf("  car (%p)...", arena + 2);
	fflush(stdout);
	printf(( (rtn = car(&ref1)) == (cell)(arena + 2)) ? " pass (%p)\n" : " FAIL (%p)\n", rtn );
	
	ref1 = (cell)arena;
	printf("  cdr (%p)...", arena + 3);
	fflush(stdout);
	printf(( (rtn = cdr(&ref1)) == (cell)(arena + 3)) ? " pass (%p)\n" : " FAIL (%p)\n", rtn );
	
	ref1 = (cell)arena;
	printf("  consp (T)...");
	fflush(stdout);
	printf( ( consp(&ref1) ) ? " pass (T)\n" : " FAIL (F)\n");

	
	printf("Chained cons cell...\n");
	arena[0] = (cell)((unsigned int) (arena + 2) | CDR_NEXT);

	ref1 = (cell)arena;
	printf("  car (%p)...", arena + 2);
	fflush(stdout);
	printf(( (rtn = car(&ref1)) == (cell)(arena + 2)) ? " pass (%p)\n" : " FAIL (%p)\n", rtn );
	
	ref1 = (cell)arena;
	printf("  cdr (%p)...", arena + 1);
	fflush(stdout);
	printf(( (rtn = cdr(&ref1)) == (cell)(arena + 1)) ? " pass (%p)\n" : " FAIL (%p)\n", rtn );
	
	ref1 = (cell)arena;
	printf("  consp (T)...");
	fflush(stdout);
	printf( ( consp(&ref1) ) ? " pass (T)\n" : " FAIL (F)\n");
	

	printf("Non-cons cell...\n");
	arena[0] = (cell)CDR_NOT_CONS;

	ref1 = (cell)arena;
	printf("  car (%p)...", NULL);
	fflush(stdout);
	printf(( (rtn = car(&ref1)) == NULL) ? " pass (%p)\n" : " FAIL (%p)\n", rtn );
	
	ref1 = (cell)arena;
	printf("  cdr (%p)...", NULL);
	fflush(stdout);
	printf(( (rtn = cdr(&ref1)) == NULL) ? " pass (%p)\n" : " FAIL (%p)\n", rtn );
	
	ref1 = (cell)arena;
	printf("  consp (F)...");
	fflush(stdout);
	printf( (! consp(&ref1) ) ? " pass (F)\n" : " FAIL (T)\n");
	
	printf("\n");	
}

void test_gc()
{
	cell arena[256];
	cell test[20];
	ext_ref guardians[5];
	cell prev_free;
	cell ptr;
	mem_start = (cell) arena;
	mem_free = (cell) arena;
	mem_end = (cell) arena+256;
	
	printf("Single cell copy...\n");
	fflush(stdout);
	prev_free = mem_free;
	test[0] = (cell)EOL;
	ptr = (cell)test;
	gc_copy(&ptr);
	printf("  mem_free (%p)... %s (%p)\n", prev_free + 1,
		(prev_free+1 == mem_free) ? "pass":"FAIL",
		mem_free);
	printf("  forwarding address (%p)... %s (%p)\n", tag(prev_free, CDR_FORW),
		(tag(prev_free, CDR_FORW) == test[0])?"pass":"FAIL",
		test[0]);
	printf("  pointer replacement (%p)... %s (%p)\n", prev_free,
		(prev_free == ptr)?"pass":"FAIL",
		ptr);
	printf("  contents (%p)... %s (%p)\n", (cell)EOL,
		((cell)EOL == *prev_free)?"pass":"FAIL",
		*prev_free);

	
	printf("Multiple cell copy...\n");
	fflush(stdout);
	prev_free = mem_free;
	test[0] = multicell_head(2,0xf0 | CDR_NOT_CONS);
	test[1] = (cell)0xdeadbeef;
	test[2] = (cell)0x00000042;
	ptr = (cell)test;
	gc_copy(&ptr);
	printf("  mem_free (%p)... %s (%p)\n", prev_free + 3,
		(prev_free+3 == mem_free) ? "pass":"FAIL",
		mem_free);
	printf("  forwarding address (%p)... %s (%p)\n", tag(prev_free, CDR_FORW),
		(tag(prev_free, CDR_FORW) == test[0])?"pass":"FAIL",
		test[0]);
	printf("  vacated space head (%p)... %s (%p)\n", multicell_head(1, EMPTY),
		(multicell_head(1,EMPTY) == test[1])?"pass":"FAIL",
		test[1]);
	printf("  pointer replacement (%p)... %s (%p)\n", prev_free,
		(prev_free == ptr)?"pass":"FAIL",
		ptr);
	printf("  contents[0] (%p)... %s (%p)\n", multicell_head(2,0xf0 | CDR_NOT_CONS),
		(multicell_head(2,0xf0 | CDR_NOT_CONS) == *prev_free)?"pass":"FAIL",
		*prev_free);
	printf("  contents[1] (%p)... %s (%p)\n", 0xdeadbeef,
		((cell)0xdeadbeef == prev_free[1])?"pass":"FAIL",
		prev_free[1]);
	printf("  contents[2] (%p)... %s (%p)\n", 0x00000042,
		((cell)0x00000042 == prev_free[2])?"pass":"FAIL",
		prev_free[2]);


	printf("Cons list copy...\n");
	fflush(stdout);
	prev_free = mem_free;
	test[0] = (cell)arena+1;
	test[1] = (cell)test+2;
	test[2] = (cell)arena+1;
	test[3] = (cell)arena;
	ptr = (cell)tag(test, CDR_NEXT);
	gc_copy(&ptr);
	printf("  mem_free (%p)... %s (%p)\n", prev_free + 3,
		(prev_free+3 == mem_free) ? "pass":"FAIL",
		mem_free);
	printf("  pointer replacement (%p)... %s (%p)\n", tag(prev_free, CDR_NEXT),
		(tag(prev_free, CDR_NEXT) == ptr)?"pass":"FAIL",
		ptr);
	printf("  forwarding address[0] (%p)... %s (%p)\n", tag(prev_free, CDR_FORW),
		(tag(prev_free, CDR_FORW) == test[0]) ? "pass":"FAIL",
		test[0]);
	printf("  vacated space head[0] (%p)... %s (%p)\n", multicell_head(0, EMPTY),
		(multicell_head(0,EMPTY) == test[1])?"pass":"FAIL",
		test[1]);
	printf("  forwarding address[1] (%p)... %s (%p)\n", tag(prev_free + 1, CDR_FORW),
		(tag(prev_free + 1, CDR_FORW) == test[2]) ? "pass":"FAIL",
		test[2]);
	printf("  vacated space head[1] (%p)... %s (%p)\n", multicell_head(0, EMPTY),
		(multicell_head(0,EMPTY) == test[3])?"pass":"FAIL",
		test[3]);
	printf("  contents[0] (%p)... %s (%p)\n", tag(arena+1, CDR_NEXT),
		(tag(arena+1, CDR_NEXT) == prev_free[0])?"pass":"FAIL",
		prev_free[0]);
	printf("  contents[1] (%p)... %s (%p)\n", arena+1,
		(arena+1 == prev_free[1])?"pass":"FAIL",
		prev_free[1]);
	printf("  contents[2] (%p)... %s (%p)\n", arena,
		(arena == prev_free[2])?"pass":"FAIL",
		prev_free[2]);

	
	printf("Guardians...\n");
	fflush(stdout);
	prev_free = mem_free;
	guardians[0].next = guardians+1;
	guardians[1].next = NULL;
	root_set = guardians;
	test[0] = (cell)arena+1;
	test[1] = (cell)arena;
	test[2] = (cell)arena+1;
	test[3] = (cell)arena;
	guardians[0].ref = (cell)test;
	guardians[1].ref = (cell)test+2;
	copy_root_set();
	printf("  car[0] (%p)... %s (%p)\n", arena+1,
		((cell)arena+1 == car(&(guardians[0].ref)))?"pass":"FAIL",
		car(&(guardians[0].ref)));
	printf("  cdr[0] (%p)... %s (%p)\n", arena,
		((cell)arena == cdr(&(guardians[0].ref)))?"pass":"FAIL",
		cdr(&(guardians[0].ref)));
	printf("  car[1] (%p)... %s (%p)\n", arena+1,
		((cell)arena+1 == car(&(guardians[1].ref)))?"pass":"FAIL",
		car(&(guardians[1].ref)));
	printf("  cdr[1] (%p)... %s (%p)\n", arena,
		((cell)arena == cdr(&(guardians[1].ref)))?"pass":"FAIL",
		cdr(&(guardians[1].ref)));
	printf("\n");


}

int main(int argc, char** argv)
{
	ext_ref list_head;
	ext_ref number_ref;
	cell test;
	int i;
	int j;
	int size;
	
	DVS_ERROR_RETURN = 1;
	
	test_forwarding();
	test_cons_access();
	test_gc();
	
	DVS_ERROR_RETURN = 0;

	init_gc();
	printf("Lisp Initialized.\n");
	list_head.next = &number_ref;
	number_ref.next = root_set;
	root_set = &list_head;
	
	list_head.ref = NULL;
	number_ref.ref = NULL;

	list_head.ref = mk_symbol("FOO!", 5);
	number_ref.ref = mk_symbol("FOO!", 5);
	printf("%p: %p, %p: %p\n", list_head.ref, *(list_head.ref), number_ref.ref, *(number_ref.ref)); 
	printf("%d %d\n", eqp(&(list_head.ref), &(number_ref.ref)), eqvp(&(list_head.ref), &(number_ref.ref)));
	list_head.ref = mk_eol();
	for(i=0; i<65000 * 20; i++)
	{
		number_ref.ref = mk_number(i);
		list_head.ref = cons(number_ref.ref, list_head.ref);
		test = list_head.ref;
	}
	fprintf(stderr, "-------------------------\n");
	while(1)
	{
		list_head.ref = mk_eol();
		for(i=0; i<65000; i++)
		{
			number_ref.ref = mk_number(i);
			list_head.ref = cons(number_ref.ref, list_head.ref);
			test = list_head.ref;
			if(i % 1000 == 0)
			{
				for(j=i; j; j--)
				{
					if(j != number_to_cint(car(&test)))
						fprintf(stderr, "bad compare: %d: %d != %d (%p)\n"
						       , i, j, (int)number_to_cint(car(&test)), 0/*car(&test)*/);
					test = cdr(&test);
				}
			}
//		printf("%d\n",i);
		}
		fprintf(stderr, "-------------------------\n");
	}
	while(1)
	{
		list_head.ref = mk_eol();
		for(i=0; i<100000; i++)
		{
			number_ref.ref = mk_number(i);
			list_head.ref = cons(number_ref.ref, list_head.ref);
			test = list_head.ref;
			if(i % 10000 == 0)
			{
				for(j=i; j; j--)
				{
					if(j != number_to_cint(car(&test))) fprintf(stderr, "bad compare!\n");
					test = cdr(&test);
				}
			}
//			printf("%d\n",i);
		}
		fprintf(stderr, "-------------------------\n");
	}
}
